using System;
using System.Configuration;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Security;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Timers;
using System.Web;
using System.Web.Mail;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml;

namespace Halide
{
	/// <summary>
	/// The SmartReader class provides a more powerful, flexible SQL reader class.
	/// </summary>
	/// <example>
	/// <code>
	/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
	/// </code>
	/// Add the following to your web.config file in order to use the Halide SmartReader.
	/// <code>
	///		&lt;connectionStrings&gt;
	///			&lt;add name="Halide"
	///			connectionString="Data Source=[server name or address]; Initial Catalog=[database name];
	///				User ID=[user name]; Password=[password]; Connection Timeout=30;"
	///			providerName="System.Data.SqlClient" /&gt;
	///		&lt;/connectionStrings&gt;
	/// </code>
	/// </example>
	public class SmartReader
	{
		private static string _version = "2008.07.03A";

		private string _SqlConnection = SqlConnectionString;

		/// <summary>
		/// Put the SmartReader into debug mode.
		/// </summary>
		private static bool Debugging = false;

		/// <summary>
		/// Return the version number of the class; read-only.
		/// </summary>
		public static string Version
		{
			get
			{
				return _version;
			}
		}

        private SqlConnection cn;
		private SqlDataReader dr;
		private SqlCommand cmd;
		private DataTable schemaTable;
		private bool schemaAvailable = false;

		/// <summary>
		/// SQL Command String.
		/// </summary>
		public string SqlCommandString = "";

		/// <summary>
		/// Determine if schema information is available.
		/// </summary>
		public bool SchemaAvailable
		{
			get
			{
				return schemaAvailable;
			}
		}

		/// <summary>
		/// Number of rows that have been read.
		/// </summary>
		public int RowsRead = 0;

		/// <summary>
		/// Create a dynamic DataTable object from a SQL data statement.
		/// This method is static, thus, must be called without instantiating a
		/// SmartReader variable. One use for this method is to create a
		/// data source for other controls that want a DataTable, like 3rd
		/// party controls.
		/// </summary>
		/// <param name="sqlcmd">SQL command string.</param>
		public static DataTable ReadTable(string sqlcmd)
		{
			DataTable outp = new DataTable();
			DateTime start = DateTime.Now;

			try
			{
				using (SqlConnection con = new SqlConnection(SqlConnectionString))
				{
					using (SqlDataAdapter da = new SqlDataAdapter(sqlcmd, con))
					{
						con.Open();
						da.Fill(outp);
					}
				}
			}
			catch { }

			if (Debugging)
				HttpContext.Current.Response.Write("ReadTable: " + sqlcmd + ": " + Halide.Temporal.DateDiff(Halide.Temporal.DateDiffComparisonType.seconds, DateTime.Now, start) + " seconds<br>");

			return (outp);
		}

		private static Exception _lastSqlError;

		/// <summary>
		/// Retrieve the last SQL error that occurred.
		/// </summary>
		/// <returns>Last SQL Exception</returns>
		public Exception SqlLastError()
		{
			return _lastSqlError;
		}

		/// <summary>
		/// Create a dynamic DataTable object from a SQL data statement.
		/// This method is static, thus, must be called without instantiating a
		/// SmartReader variable. One use for this method is to create a
		/// data source for other controls that want a DataTable, like 3rd
		/// party controls.
		/// </summary>
		/// <param name="sqlcmd">SQL command string.</param>
		/// <param name="connectionName">Name of a connection string within the Web.Config file.</param>
		public static DataTable ReadTable(string sqlcmd, string connectionName)
		{
			DataTable outp = new DataTable();
			DateTime start = DateTime.Now;

			try
			{
				using (SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings[connectionName].ConnectionString))
				{
					using (SqlDataAdapter da = new SqlDataAdapter(sqlcmd, con))
					{
						con.Open();
						da.Fill(outp);
					}
				}
			}

			catch(Exception err)
			{
                _lastSqlError = err;
			}

			if (Debugging)
			{
				HttpContext.Current.Response.Write("ReadTable: " + sqlcmd + ": " + Halide.Temporal.DateDiff(Halide.Temporal.DateDiffComparisonType.seconds, DateTime.Now, start) + " seconds<br>");
			}

			return (outp);
		}

		private static string SqlConnectionString
		{
			get
			{
				string CS = "";

				try
				{
					CS = ConfigurationManager.ConnectionStrings["Halide"].ConnectionString;
				}

				catch
				{
					if (String.IsNullOrEmpty(CS))
					{
						CS = "";
					}
				}

				return CS;
			}
		}

		/// <summary>
		/// Select the database connection string.
		/// </summary>
		/// <param name="connection">Name of the connection string within the web.config file.</param>
		public void SetSqlConnection(string connection)
		{
			_SqlConnection = ConfigurationManager.ConnectionStrings[connection].ConnectionString;
		}

		/// <summary>
		/// Get the current SQL connection string.
		/// </summary>
		private string GetSqlConnection()
		{
			if (String.IsNullOrEmpty(_SqlConnection))
			{
				_SqlConnection = SqlConnectionString;
			}

			return _SqlConnection;
		}

		/// <summary>
		/// Instantiate the class by passing a SQL command string, where clause, order by clause,
		/// rows per page, and page number, to receive only that page's rows. Only compatible with
		/// Microsoft SQL Server 2005 and later.
		/// 
		/// If you insert [WHERECLAUSE] within your SELECT statement, you can force the passed
		/// where clause to be inserted for you; for advanced queries. The same holds true for
		/// [ORDERBY]. Remember NOT to include the "WHERE" or "ORDER BY" text in your passed arguments.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test", "ID>5", "LastName ASC, FirstName ASC", 10, 3);
		/// </code>
		/// </example>
		/// <param name="CommandText">A SQL command to execute.</param>
		public SmartReader(string CommandText, string WhereClause, string OrderByClause, Int32 PerPage, Int32 PageNum)
		{
			string template = "WITH VIRTUAL_HALIDE_ROWSET AS (SELECT ROW_NUMBER() OVER( ORDER BY [ORDERBY] ) AS ROWNUMBER, * FROM ([SELECT]) AS ML_HALIDE WHERE [WHERECLAUSE]) SELECT * FROM VIRTUAL_HALIDE_ROWSET WHERE ROWNUMBER BETWEEN CONVERT(varchar, [BEGINRECORD]) and CONVERT(varchar, [ENDRECORD]);";

			if (PerPage > 0 && PageNum > 0)
			{
				template = template.Replace("[ORDERBY]", OrderByClause);
				template = template.Replace("[SELECT]", CommandText);
				template = template.Replace("[WHERECLAUSE]", WhereClause);

				Int32 beginRecord = (PerPage * PageNum) - PerPage + 1;
				Int32 endRecord = (PerPage * PageNum);

				template = template.Replace("[BEGINRECORD]", beginRecord.ToString());
				template = template.Replace("[ENDRECORD]", endRecord.ToString());

				SqlCommandString = template;

				try
				{
					cn = new SqlConnection();
					cn.ConnectionString = GetSqlConnection();
					cn.Open();
					cmd = new SqlCommand();
					cmd.Connection = cn;
					cmd.CommandText = template;
					dr = cmd.ExecuteReader();
				}

				catch (Exception err)
				{
					_lastSqlError = err;
				}
			}
		}

		/// <summary>
		/// Instantiate the class by passing a SQL command string.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// </code>
		/// </example>
		/// <param name="CommandText">A SQL command to execute.</param>
		public SmartReader(string CommandText)
		{
			SqlCommandString = CommandText;

			try
			{
				cn = new SqlConnection();
				cn.ConnectionString = GetSqlConnection();
				cn.Open();
				cmd = new SqlCommand();
				cmd.Connection = cn;
				cmd.CommandText = CommandText;
				dr = cmd.ExecuteReader();
			}

			catch (Exception err)
			{
				_lastSqlError = err;
			}
		}

		/// <summary>
		/// Instantiate the class by passing a SQL command string. Optionally enables getting schema information.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// </code>
		/// </example>
		/// <param name="CommandText">A SQL command to execute.</param>
		/// <param name="GetSchema">Determines if schema information should also be retured, enabling various other methods.</param>
		public SmartReader(string CommandText, bool GetSchema)
		{
			SqlCommandString = CommandText;

			try
			{
				cn = new SqlConnection();
				cn.ConnectionString = GetSqlConnection();
				cn.Open();
				cmd = new SqlCommand();
				cmd.Connection = cn;
				cmd.CommandText = CommandText;

				if (GetSchema)
				{
					dr = cmd.ExecuteReader(CommandBehavior.SchemaOnly);
					schemaTable = dr.GetSchemaTable();
					dr.Close();

					schemaAvailable = true;
				}
				
				dr = cmd.ExecuteReader();
			}

			catch (Exception err)
			{
				_lastSqlError = err;
			}
		}

		/// <summary>
		/// Instantiate the class by passing a SQL command string and connection string name.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test", "Halide");
		/// </code>
		/// </example>
		/// <param name="CommandText">A SQL command to execute.</param>
		/// <param name="connectionName">Name of a connection string within the Web.Config file.</param>
		public SmartReader(string CommandText, string connectionName)
		{
			SqlCommandString = CommandText;

			try
			{
				SetSqlConnection(connectionName);

				cn = new SqlConnection();
				cn.ConnectionString = GetSqlConnection();
				cn.Open();
				cmd = new SqlCommand();
				cmd.Connection = cn;
				cmd.CommandText = CommandText;
				dr = cmd.ExecuteReader();
			}

			catch (Exception err)
			{
				_lastSqlError = err;
			}
		}

		/// <summary>
		/// Instantiate the class by passing a SQL command string, optionally enables getting schema information,
		/// pass a connection string name.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// </code>
		/// </example>
		/// <param name="CommandText">A SQL command to execute.</param>
		/// <param name="GetSchema">Determines if schema information should also be retured, enabling various other methods.</param>
		/// <param name="connectionName">Name of a connection string within the Web.Config file.</param>
		public SmartReader(string CommandText, bool GetSchema, string connectionName)
		{
			SqlCommandString = CommandText;

			try
			{
				SetSqlConnection(connectionName);

				cn = new SqlConnection();
				cn.ConnectionString = GetSqlConnection();
				cn.Open();
				cmd = new SqlCommand();
				cmd.Connection = cn;
				cmd.CommandText = CommandText;

				if (GetSchema)
				{
					dr = cmd.ExecuteReader(CommandBehavior.SchemaOnly);
					schemaTable = dr.GetSchemaTable();
					dr.Close();

					schemaAvailable = true;
				}

				dr = cmd.ExecuteReader();
			}

			catch (Exception err)
			{
				_lastSqlError = err;
			}
		}

		/// <summary>
		/// Closes the Smart Reader object when the class is destroyed.
		/// </summary>
		~SmartReader()
		{
			Close();
		}

		/// <summary>
		/// Close a previously opened SmartReader object. You must close an opened SmartReader object when finished using it!
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Close();
		/// </code>
		/// </example>
		/// <returns>Nothing.</returns>
		public void Close()
		{
			try
			{
				cn.Dispose();
				cmd.Dispose();
			}

			catch (Exception err)
			{
				_lastSqlError = err;
			}
		}

		/// <summary>
		/// Read the first (or next) row in the query.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// while (reader.Read())
		/// {
		///		...
		///	}
		///	reader.Close();
		/// </code>
		/// </example>
		/// <returns>true if read, false if end of data.</returns>
		public bool Read()
		{
			bool output = false;

			try
			{
				output = dr.Read();

				if (output)
				{
					RowsRead++;
				}
			}

			catch (Exception err)
			{
				_lastSqlError = err;
			}

			return output;
		}

		/// <summary>
		/// Switch to the next result set; for use with SQL statements that return multiple (compound) recordsets.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.NextResult();
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <returns>true if result set is switched, false if not.</returns>
		public bool NextResult()
		{
			try
			{
				return dr.NextResult();
			}

			catch (Exception err)
			{
				_lastSqlError = err;
				return false;
			}
		}

		/// <summary>
		/// Shorthand for the GetString method. Returns a record as a string by name.
		/// </summary>
		/// <value>
		/// Record value as a string.
		/// </value>
		public string this[string ColumnName]
		{
			get
			{
				return GetString(ColumnName);
			}
		}

		/// <summary>
		/// Shorthand for the GetString method. Returns a record as a string by column number.
		/// </summary>
		/// <value>
		/// Record value as a string.
		/// </value>
		public string this[int ColumnIndex]
		{
			get
			{
				return GetString(ColumnIndex);
			}
		}

		/// <summary>
		/// Read a column value in as a DateTime object.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// DateTime reader.GetDateTime("thedate");
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <param name="ColumnName">Name of column to retrieve.</param>
		/// <returns>DateTime object.</returns>
		public DateTime GetDateTime(string ColumnName)
		{
			DateTime output = new DateTime(1900, 1, 1);
			int ord = dr.GetOrdinal(ColumnName);

			try
			{
				output = dr.GetDateTime(ord);
			}

			catch (Exception err)
			{
				_lastSqlError = err;
			}

			return output;
		}

		/// <summary>
		/// Read a column value in as a string formatted as a date.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// string result = reader.GetDate("thedate");
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <param name="ColumnName">Name of column to retrieve</param>
		/// <returns>A string with the column value as a date.</returns>
		public string GetDate(string ColumnName)
		{
			string output = string.Empty;

			try
			{
				int ord = dr.GetOrdinal(ColumnName);

				if (dr.IsDBNull(ord)) output = "";
				else output = dr.GetDateTime(ord).ToString("M/d/yyyy");
			}

			catch (Exception err)
			{
				_lastSqlError = err;
			}

			return output;
		}

		/// <summary>
		/// Read a column value in as a string formatted as a time.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// string result = reader.GetTime("thedate");
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <param name="ColumnName">Name of column to retrieve.</param>
		/// <returns>A string with the column value as a time.</returns>
		public string GetTime(string ColumnName)
		{
			string output = string.Empty;

			try
			{
				int ord = dr.GetOrdinal(ColumnName);

				if (dr.IsDBNull(ord)) output = "";
				else output = dr.GetDateTime(ord).ToString("h:mm tt");
			}

			catch (Exception err)
			{
				_lastSqlError = err;
			}

			return output;
		}

		/// <summary>
		/// Read a column value in as a string formatted as money.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// string result = reader.GetMoney("cost");
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <param name="ColumnName">Name of column to retrieve.</param>
		/// <returns>A string with the column formatted as currency.</returns>
		public string GetMoney(string ColumnName)
		{
			string output = string.Empty;

			try
			{
				int ord = dr.GetOrdinal(ColumnName);

				if (dr.IsDBNull(ord)) output = "";
				else output = dr.GetSqlMoney(ord).ToDouble().ToString("0.00");
			}

			catch (Exception err)
			{
				_lastSqlError = err;
			}

			return output;
		}

		/// <summary>
		/// Read a column value in as an integer.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// int result = reader.GetInt("ID");
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <param name="ColumnName">Name of column to retrieve.</param>
		/// <returns>An integer.</returns>
		public int GetInt(string ColumnName)
		{
			int output = 0;

			try
			{
				int ord = dr.GetOrdinal(ColumnName);

				if (dr.IsDBNull(ord)) output = 0;
				//else if (dr.GetDataTypeName(ord) == "tinyint") output = Convert.ToInt32(dr.GetByte(ord));
				else output = Convert.ToInt32(dr[ord].ToString());
				//else output = dr.GetInt32(ord);
			}

			catch (Exception err)
			{
				_lastSqlError = err;
			}

			return output;
		}

		/// <summary>
		/// Read a column value in as a long.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// long result = reader.GetLong("ID");
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <param name="ColumnName">Name of column to retrieve.</param>
		/// <returns>A long.</returns>
		public long GetLong(string ColumnName)
		{
			long output = 0;

			try
			{
				int ord = dr.GetOrdinal(ColumnName);

				if (dr.IsDBNull(ord)) output = 0;
				else output = Convert.ToInt64(dr[ord].ToString());
			}

			catch (Exception err)
			{
				_lastSqlError = err;
			}

			return output;
		}

		/// <summary>
		/// Retrieve the SQL data type name of a specified column (eg. "varchar", "int", "smallint", et al.).
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// string result = reader.GetDataTypeName(0);
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <param name="ColumnIndex">Number of the column.</param>
		/// <returns>A string.</returns>
		public string GetDataTypeName(int ColumnIndex)
		{
			string output = string.Empty;

			try
			{
				output = dr.GetDataTypeName(ColumnIndex);
			}

			catch (Exception err)
			{
				_lastSqlError = err;
			}

			return output;
		}

		/// <summary>
		/// Retrieve the SQL data type name of a specified column (eg. "varchar", "int", "smallint", et al.).
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// string result = reader.GetDataTypeName("ID");
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <param name="ColumnName">Name of the column.</param>
		/// <returns>A string.</returns>
		public string GetDataTypeName(string ColumnName)
		{
			try
			{
				return GetDataTypeName(dr.GetOrdinal(ColumnName));
			}

			catch (Exception err)
			{
				_lastSqlError = err;
				return "";
			}
		}

		/// <summary>
		/// Retrieve the columns and their schema information as an ASCII text string for output.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// Response.Write (reader.ListSchema().Replace("\r\n", "&lt;br /&gt;"));
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <returns>A string</returns>
		public string ListSchema()
		{
			string output = "Schema information is not available. Instantiate the Smartreader with a true value to use schema-based features.";

			if (schemaAvailable)
			{
				output = "";

				try
				{
					foreach (System.Data.DataRow myField in schemaTable.Rows)
					{
						//For each property of the field...
						foreach (System.Data.DataColumn myProperty in schemaTable.Columns)
						{
							//Display the field name and value.
							output += myProperty.ColumnName + " = " + myField[myProperty].ToString() + "\r\n";
						}

						output += "\r\n";
					}
				}

				catch (Exception err)
				{
					_lastSqlError = err;
				}
			}

			return output;
		}

		/// <summary>
		/// Get the primary key column name, or an empty string if schema is not available.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// string result = reader.GetPrimaryKeyName();
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <returns>A string with the column name of the primary key.</returns>
		public string GetPrimaryKeyName()
		{
			if (schemaAvailable)
			{
				try
				{
					// Find by iteration
					foreach (System.Data.DataRow myField in schemaTable.Rows)
					{
						foreach (System.Data.DataColumn myProperty in schemaTable.Columns)
						{
							if (myProperty.ColumnName.ToLower() == "iskey")
								return myField["ColumnName"].ToString();
						}
					}
				}

				catch (Exception err)
				{
					_lastSqlError = err;
				}
			}

			return "";
		}


		/// <summary>
		/// Get the size (dimension) of the column, or zero if no schema information is available.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// int result = reader.GetColumnSize("ID");
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <param name="ColumnName">Name of the column.</param>
		/// <returns>An integer.</returns>
		public int GetColumnSize(string ColumnName)
		{
			int output = 0;

			if (schemaAvailable)
			{
				try
				{
					System.Data.DataColumn[] myPrimaryKey = new System.Data.DataColumn[1];
					myPrimaryKey[0] = schemaTable.Columns["ColumnName"];
					schemaTable.PrimaryKey = myPrimaryKey;
					System.Data.DataRow myDataRow = schemaTable.Rows.Find(ColumnName);
					output = (int)myDataRow["ColumnSize"];
				}

				catch (Exception err)
				{
					_lastSqlError = err;
				}
			}

			/*
			// Find by iteration
			foreach (DataRow myField in schemaTable.Rows)
			{
				if (myField["ColumnName"].ToString().ToLower() == ColumnName.ToLower())
				{
					//For each property of the field...
					foreach (DataColumn myProperty in schemaTable.Columns)
					{
						if (myProperty.ColumnName.ToLower() == "columnsize")
							return (int)myField[myProperty];
					}
				}
			}
			*/

			return output;
		}

		/// <summary>
		/// Is the specified column a primary key? Returns false if no schema information is available.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// bool result = reader.GetColumnIsKey("ID");
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <param name="ColumnName">Name of the column.</param>
		/// <returns>true or false.</returns>
		public bool GetColumnIsKey(string ColumnName)
		{
			bool output = false;

			if (schemaAvailable)
			{
				try
				{
					if (GetPrimaryKeyName().ToLower() == ColumnName.ToLower())
					{
						output = true;
					}
				}

				catch (Exception err)
				{
					_lastSqlError = err;
				}
			}

			return output;
		}
		
		/// <summary>
		/// Does a specified column allow nulls? Returns false if no schema information is available.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// bool result = reader.GetColumnAllowNulls("ID");
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <param name="ColumnName">Name of the column.</param>
		/// <returns>true or false.</returns>
		public bool GetColumnAllowNulls(string ColumnName)
		{
			bool output = false;

			if (schemaAvailable)
			{
				try
				{
					System.Data.DataColumn[] myPrimaryKey = new System.Data.DataColumn[1];
					myPrimaryKey[0] = schemaTable.Columns["ColumnName"];
					schemaTable.PrimaryKey = myPrimaryKey;
					System.Data.DataRow myDataRow = schemaTable.Rows.Find(ColumnName);
					output = (bool)myDataRow["AllowDBNull"];
				}

				catch (Exception err)
				{
					_lastSqlError = err;
				}
			}

			return output;
		}

		/// <summary>
		/// Is a specified column an identity? Returns false if no schema information is available.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// bool result = reader.GetColumnIsIdentity("ID");
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <param name="ColumnName">Name of the column.</param>
		/// <returns>true or false.</returns>
		public bool GetColumnIsIdentity(string ColumnName)
		{
			bool output = false;

			if (schemaAvailable)
			{
				try
				{
					System.Data.DataColumn[] myPrimaryKey = new System.Data.DataColumn[1];
					myPrimaryKey[0] = schemaTable.Columns["ColumnName"];
					schemaTable.PrimaryKey = myPrimaryKey;
					System.Data.DataRow myDataRow = schemaTable.Rows.Find(ColumnName);
					output = (bool)myDataRow["IsIdentity"];
				}

				catch (Exception err)
				{
					_lastSqlError = err;
				}
			}

			return output;
		}

		/// <summary>
		/// Is a specified column an auto-incrementing field? Returns false if no schema information is available.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// bool result = reader.GetColumnIsAutoIncrement("ID");
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <param name="ColumnName">Name of the column.</param>
		/// <returns>true or false.</returns>
		public bool GetColumnIsAutoIncrement(string ColumnName)
		{
			bool output = false;

			if (schemaAvailable)
			{
				try
				{
					System.Data.DataColumn[] myPrimaryKey = new System.Data.DataColumn[1];
					myPrimaryKey[0] = schemaTable.Columns["ColumnName"];
					schemaTable.PrimaryKey = myPrimaryKey;
					System.Data.DataRow myDataRow = schemaTable.Rows.Find(ColumnName);
					output = (bool)myDataRow["IsAutoIncrement"];
				}

				catch (Exception err)
				{
					_lastSqlError = err;
				}
			}

			return output;
		}

		/// <summary>
		/// Return a column's equivalent system data type name (eg. "String", "Int64", et al.).
		/// Returns an empty string if no schema information is available.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// string result = reader.GetColumnSystemDataType("ID");
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <param name="ColumnName">Name of the column.</param>
		/// <returns>Data type name as a string.</returns>
		public string GetColumnSystemDataType(string ColumnName)
		{
			string output = "";

			if (schemaAvailable)
			{
				try
				{
					System.Data.DataColumn[] myPrimaryKey = new System.Data.DataColumn[1];
					myPrimaryKey[0] = schemaTable.Columns["ColumnName"];
					schemaTable.PrimaryKey = myPrimaryKey;
					System.Data.DataRow myDataRow = schemaTable.Rows.Find(ColumnName);
					output = myDataRow["DataType"].ToString();
				}

				catch (Exception err)
				{
					_lastSqlError = err;
				}
			}

			return output;
		}

		/// <summary>
		/// Read a column value in as a double.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// double result = reader.GetDouble("ID");
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <param name="ColumnName">Name of column to retrieve.</param>
		/// <returns>A double.</returns>
		public double GetDouble(string ColumnName)
		{
			double output = 0;

			try
			{
				int ord = dr.GetOrdinal(ColumnName);

				if (dr.IsDBNull(ord)) output = 0;
				else output = Convert.ToDouble(dr[ord].ToString());
			}

			catch (Exception err)
			{
				_lastSqlError = err;
			}

			return output;
		}

		/// <summary>
		/// Read a column value in as a decimal.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// decimal result = reader.GetDecimal("ID");
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <param name="ColumnName">Name of column to retrieve.</param>
		/// <returns>A decimal.</returns>
		public decimal GetDecimal(string ColumnName)
		{
			decimal output = 0;

			try
			{
				int ord = dr.GetOrdinal(ColumnName);

				if (dr.IsDBNull(ord)) output = 0;
				else output = Convert.ToDecimal(dr[ord].ToString());
			}

			catch (Exception err)
			{
				_lastSqlError = err;
			}

			return output;
		}

	    /// <summary>
	    /// Read a column value in as a Int64.
	    /// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// Int64 result = reader.GetInt64("ID");
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <param name="ColumnName">Name of column to retrieve.</param>
	    /// <returns>An Int64.</returns>
	    public Int64 GetInt64(string ColumnName)
	    {
	        Int64 output = 0;

			try
			{
				int ord = dr.GetOrdinal(ColumnName);

				if (dr.IsDBNull(ord)) output = 0;
				else output = Convert.ToInt64(dr[ord].ToString());
			}

			catch (Exception err)
			{
				_lastSqlError = err;
			}

	        return output;
	    }

		/// <summary>
		/// Read a column value in as a float.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// float result = reader.GetFloat("ID");
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <param name="ColumnName">Name of column to retrieve.</param>
		/// <returns>A float.</returns>
		public float GetFloat(string ColumnName)
		{
			float output = 0;

			try
			{
				int ord = dr.GetOrdinal(ColumnName);

				if (dr.IsDBNull(ord)) output = 0;
				else output = dr.GetFloat(ord);
			}

			catch (Exception err)
			{
				_lastSqlError = err;
			}

			return output;
		}

		/// <summary>
		/// Read a column value in as a string, by its column number.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// string result = reader.GetString(0);
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <param name="ColumnIndex">Number of column to retrieve.</param>
		/// <returns>A string.</returns>
		public string GetString(int ColumnIndex)
		{
			string output = string.Empty;

			try
			{
				if (dr.IsDBNull(ColumnIndex)) output = "";
				else output = dr[ColumnIndex].ToString();
			}

			catch (Exception err)
			{
				_lastSqlError = err;
			}

			return output;
		}

		/// <summary>
		/// Read a column value in as a string, by its column name.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// string result = reader.GetString("ID");
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <param name="ColumnName">Name of column to retrieve.</param>
		/// <returns>A string.</returns>
		public string GetString(string ColumnName)
		{
			try
			{
				return GetString(dr.GetOrdinal(ColumnName));
			}

			catch (Exception err)
			{
				_lastSqlError = err;
				return "";
			}
		}

		/// <summary>
		/// Read a column value in as a boolean, by its column name.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// bool result = reader.GetBoolean("ID");
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <param name="ColumnName">Name of column to retrieve.</param>
		/// <returns>A boolean.</returns>
		public bool GetBoolean(string ColumnName)
		{
			bool output = false;

			try
			{
				int ord = dr.GetOrdinal(ColumnName);

				if (dr.IsDBNull(ord)) output = false;
				else output = dr.GetBoolean(ord);
			}

			catch (Exception err)
			{
				_lastSqlError = err;
			}

			return output;
		}

		/// <summary>
		/// Read a column value in and process as "Yes", "No", or "n/a" using the GetBoolean method.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// string result = reader.GetYesNo("choice");
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <param name="ColumnName">Name of column to retrieve.</param>
		/// <returns>A string.</returns>
		public string GetYesNo(string ColumnName)
		{
			string output = "n/a";

			try
			{
				int ord = dr.GetOrdinal(ColumnName);

				if (dr.IsDBNull(ord)) output = "n/a";
				else if (dr.GetBoolean(ord)) output = "Yes";
				else output = "No";
			}

			catch (Exception err)
			{
				_lastSqlError = err;
			}

			return output;
		}

		/// <summary>
		/// Get a column name by its number.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// string result = reader.ColumnName(0);
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <param name="ColumnNumber">Number of column to retrieve.</param>
		/// <returns>A string.</returns>
		public string ColumnName(int ColumnNumber)
		{
			try
			{
				return dr.GetName(ColumnNumber);
			}

			catch (Exception err)
			{
				_lastSqlError = err;
				return "";
			}
		}

		/// <summary>
		/// Returns true if there are rows to return.
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// bool result = reader.HasRows;
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <value>A boolean representing the availability of rows to read.</value>
		public bool HasRows
		{
			get
			{
				try
				{
					return dr.HasRows;
				}

				catch (Exception err)
				{
					_lastSqlError = err;
					return false;
				}
			}
		}

		/// <summary>
		/// Is a specified column value empty or null?
		/// </summary>
		/// <example>
		/// <code>
		/// Halide.SmartReader reader = new Halide.SmartReader("select * from test");
		/// reader.Read();
		/// bool result = reader.IsNullOrEmpty("ID");
		/// ...
		/// reader.Close();
		/// </code>
		/// </example>
		/// <param name="ColumnName">Name of column to evaluate.</param>
		/// <returns>true if column value is null or empty, false if not.</returns>
		public bool IsNullOrEmpty(string ColumnName)
		{
			try
			{
				return String.IsNullOrEmpty(GetString(dr.GetOrdinal(ColumnName)));
			}

			catch (Exception err)
			{
				_lastSqlError = err;
				return false;
			}
		}

		/// <summary>
		/// Returns the number of fields in a recordset.
		/// </summary>
		/// <value>An integer.</value>
		public int FieldCount
		{
			get
			{
				try
				{
					return dr.FieldCount;
				}

				catch (Exception err)
				{
					_lastSqlError = err;
					return 0;
				}
			}
		}

		// End of SmartReader class
	}
	
	// End of namespace
}